├── LICENSE ├── README.md ├── package.json ├── plugin.xml ├── src ├── android │ └── UniqueDeviceID.java ├── ios │ ├── CDVUniqueDeviceID.h │ ├── CDVUniqueDeviceID.m │ ├── UICKeyChainStore.h │ └── UICKeyChainStore.m └── wp │ └── UniqueDeviceID.cs └── www └── uniqueid.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Paldom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | UniqueDeviceID 2 | ============== 3 | 4 | PhoneGap / Cordova unique device id (UUID) plugin for Android, iOS and Windows Phone 8. Remains the same after app uninstall. 5 | 6 | ## Installation 7 | 8 | Latest stable release: ```phonegap local plugin add cordova-plugin-uniquedeviceid``` 9 | or ```cordova plugin add cordova-plugin-uniquedeviceid``` 10 | 11 | Current state from git: ```phonegap local plugin add https://github.com/Paldom/UniqueDeviceID.git``` 12 | or ```cordova plugin add https://github.com/Paldom/UniqueDeviceID.git``` 13 | 14 | ## Installation - PhoneGap Build 15 | 16 | Add following to config.xml: `````` 17 | 18 | For older versions, use the following: `````` 19 | or `````` 20 | 21 | ## Supported Platforms 22 | 23 | - Android 24 | - iOS 25 | - Windows Phone 8 26 | 27 | ## Usage 28 | 29 | // Get UUID 30 | window.plugins.uniqueDeviceID.get(success, fail); 31 | 32 | Success callback function: 33 | 34 | function success(uuid) 35 | { 36 | console.log(uuid); 37 | }; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-uniquedeviceid", 3 | "version": "1.3.1", 4 | "description": "PhoneGap / Cordova unique device id (UUID) plugin for Android, iOS and Windows Phone 8. Remains the same after app uninstall.", 5 | "cordova": { 6 | "id": "cordova-plugin-uniquedeviceid", 7 | "platforms": [ 8 | "ios", 9 | "android", 10 | "wp8" 11 | ] 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Paldom/UniqueDeviceID.git" 16 | }, 17 | "keywords": [ 18 | "phonegap", 19 | "cordova", 20 | "android", 21 | "ios", 22 | "wp", 23 | "Windows", 24 | "phone", 25 | "device", 26 | "id", 27 | "uuid", 28 | "unique" 29 | ], 30 | "engines": [ 31 | { 32 | "name": "cordova", 33 | "version": ">=3.0.0" 34 | } 35 | ], 36 | "author": "Domonkos Pal (https://github.com/Paldom)", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/Paldom/UniqueDeviceID/issues" 40 | }, 41 | "homepage": "https://github.com/Paldom/UniqueDeviceID#readme" 42 | } 43 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | UniqueDeviceID 9 | 10 | 11 | PhoneGap / Cordova unique device id (UUID) plugin for Android, iOS and Windows Phone 8. Remains the same after app uninstall. 12 | 13 | MIT 14 | phonegap,cordova,android,ios,wp,Windows,phone,device,id,uuid,unique 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/android/UniqueDeviceID.java: -------------------------------------------------------------------------------- 1 | package hu.dpal.phonegap.plugins; 2 | 3 | import org.apache.cordova.CallbackContext; 4 | import org.apache.cordova.CordovaPlugin; 5 | import org.apache.cordova.CordovaWebView; 6 | import org.json.JSONArray; 7 | import org.json.JSONException; 8 | 9 | import android.Manifest; 10 | import android.content.Context; 11 | import android.content.pm.PackageManager; 12 | import android.provider.Settings.Secure; 13 | import android.telephony.TelephonyManager; 14 | import android.util.Log; 15 | 16 | import java.lang.reflect.Method; 17 | 18 | public class UniqueDeviceID extends CordovaPlugin { 19 | 20 | public static final String TAG = "UniqueDeviceID"; 21 | public CallbackContext callbackContext; 22 | public static final int REQUEST_READ_PHONE_STATE = 0; 23 | 24 | protected final static String permission = Manifest.permission.READ_PHONE_STATE; 25 | 26 | @Override 27 | public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { 28 | this.callbackContext = callbackContext; 29 | try { 30 | if (action.equals("get")) { 31 | if(this.hasPermission(permission)){ 32 | getDeviceId(); 33 | }else{ 34 | this.requestPermission(this, REQUEST_READ_PHONE_STATE, permission); 35 | } 36 | }else { 37 | this.callbackContext.error("Invalid action"); 38 | return false; 39 | } 40 | }catch(Exception e ) { 41 | this.callbackContext.error("Exception occurred: ".concat(e.getMessage())); 42 | return false; 43 | } 44 | return true; 45 | 46 | } 47 | 48 | public void onRequestPermissionResult(int requestCode, String[] permissions, 49 | int[] grantResults) throws JSONException { 50 | if(requestCode == REQUEST_READ_PHONE_STATE){ 51 | getDeviceId(); 52 | } 53 | } 54 | 55 | protected void getDeviceId(){ 56 | try { 57 | Context context = cordova.getActivity().getApplicationContext(); 58 | TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 59 | 60 | String uuid; 61 | String androidID = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); 62 | String deviceID = tm.getDeviceId(); 63 | String simID = tm.getSimSerialNumber(); 64 | 65 | if ("9774d56d682e549c".equals(androidID) || androidID == null) { 66 | androidID = ""; 67 | } 68 | 69 | if (deviceID == null) { 70 | deviceID = ""; 71 | } 72 | 73 | if (simID == null) { 74 | simID = ""; 75 | } 76 | 77 | uuid = androidID + deviceID + simID; 78 | uuid = String.format("%32s", uuid).replace(' ', '0'); 79 | uuid = uuid.substring(0, 32); 80 | uuid = uuid.replaceAll("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"); 81 | 82 | this.callbackContext.success(uuid); 83 | }catch(Exception e ) { 84 | this.callbackContext.error("Exception occurred: ".concat(e.getMessage())); 85 | } 86 | } 87 | 88 | private boolean hasPermission(String permission) throws Exception{ 89 | boolean hasPermission = true; 90 | Method method = null; 91 | try { 92 | method = cordova.getClass().getMethod("hasPermission", permission.getClass()); 93 | Boolean bool = (Boolean) method.invoke(cordova, permission); 94 | hasPermission = bool.booleanValue(); 95 | } catch (NoSuchMethodException e) { 96 | Log.w(TAG, "Cordova v" + CordovaWebView.CORDOVA_VERSION + " does not support API 23 runtime permissions so defaulting to GRANTED for " + permission); 97 | } 98 | return hasPermission; 99 | } 100 | 101 | private void requestPermission(CordovaPlugin plugin, int requestCode, String permission) throws Exception{ 102 | try { 103 | java.lang.reflect.Method method = cordova.getClass().getMethod("requestPermission", org.apache.cordova.CordovaPlugin.class ,int.class, java.lang.String.class); 104 | method.invoke(cordova, plugin, requestCode, permission); 105 | } catch (NoSuchMethodException e) { 106 | throw new Exception("requestPermission() method not found in CordovaInterface implementation of Cordova v" + CordovaWebView.CORDOVA_VERSION); 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /src/ios/CDVUniqueDeviceID.h: -------------------------------------------------------------------------------- 1 | // 2 | // CDVUniqueDeviceID.h 3 | // 4 | // 5 | // 6 | 7 | #import 8 | #import 9 | 10 | @interface CDVUniqueDeviceID : CDVPlugin 11 | 12 | - (void)get:(CDVInvokedUrlCommand*)command; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /src/ios/CDVUniqueDeviceID.m: -------------------------------------------------------------------------------- 1 | // 2 | // CDVUniqueDeviceID.m 3 | // 4 | // 5 | // 6 | 7 | #import "CDVUniqueDeviceID.h" 8 | #import "UICKeyChainStore.h" 9 | 10 | @implementation CDVUniqueDeviceID 11 | 12 | -(void)get:(CDVInvokedUrlCommand*)command 13 | { 14 | [self.commandDelegate runInBackground:^{ 15 | 16 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 17 | NSString *uuidUserDefaults = [defaults objectForKey:@"uuid"]; 18 | 19 | NSString *uuid = [UICKeyChainStore stringForKey:@"uuid"]; 20 | 21 | if ( uuid && !uuidUserDefaults) { 22 | [defaults setObject:uuid forKey:@"uuid"]; 23 | [defaults synchronize]; 24 | 25 | } else if ( !uuid && !uuidUserDefaults ) { 26 | NSString *uuidString = [[NSUUID UUID] UUIDString]; 27 | 28 | [UICKeyChainStore setString:uuidString forKey:@"uuid"]; 29 | 30 | [defaults setObject:uuidString forKey:@"uuid"]; 31 | [defaults synchronize]; 32 | 33 | uuid = [UICKeyChainStore stringForKey:@"uuid"]; 34 | 35 | } else if ( ![uuid isEqualToString:uuidUserDefaults] ) { 36 | [UICKeyChainStore setString:uuidUserDefaults forKey:@"uuid"]; 37 | uuid = [UICKeyChainStore stringForKey:@"uuid"]; 38 | } 39 | 40 | CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:uuid]; 41 | [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; 42 | }]; 43 | } 44 | 45 | @end 46 | 47 | -------------------------------------------------------------------------------- /src/ios/UICKeyChainStore.h: -------------------------------------------------------------------------------- 1 | // 2 | // UICKeyChainStore.h 3 | // UICKeyChainStore 4 | // 5 | // Created by Kishikawa Katsumi on 11/11/20. 6 | // Copyright (c) 2011 Kishikawa Katsumi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UICKeyChainStore : NSObject 12 | 13 | @property (nonatomic, readonly) NSString *service; 14 | @property (nonatomic, readonly) NSString *accessGroup; 15 | 16 | + (NSString *)defaultService; 17 | + (void)setDefaultService:(NSString *)defaultService; 18 | 19 | + (UICKeyChainStore *)keyChainStore; 20 | + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service; 21 | + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service accessGroup:(NSString *)accessGroup; 22 | 23 | - (instancetype)init; 24 | - (instancetype)initWithService:(NSString *)service; 25 | - (instancetype)initWithService:(NSString *)service accessGroup:(NSString *)accessGroup; 26 | 27 | + (NSString *)stringForKey:(NSString *)key; 28 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service; 29 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup; 30 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key; 31 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service; 32 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup; 33 | 34 | + (NSData *)dataForKey:(NSString *)key; 35 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service; 36 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup; 37 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key; 38 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service; 39 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup; 40 | 41 | - (void)setString:(NSString *)string forKey:(NSString *)key; 42 | - (NSString *)stringForKey:(NSString *)key; 43 | 44 | - (void)setData:(NSData *)data forKey:(NSString *)key; 45 | - (NSData *)dataForKey:(NSString *)key; 46 | 47 | + (BOOL)removeItemForKey:(NSString *)key; 48 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service; 49 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup; 50 | + (BOOL)removeAllItems; 51 | + (BOOL)removeAllItemsForService:(NSString *)service; 52 | + (BOOL)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup; 53 | 54 | - (void)removeItemForKey:(NSString *)key; 55 | - (void)removeAllItems; 56 | 57 | - (void)synchronize; 58 | 59 | // object subscripting 60 | 61 | - (NSString *)objectForKeyedSubscript:(NSString *)key; 62 | - (void)setObject:(NSString *)obj forKeyedSubscript:(NSString *)key; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /src/ios/UICKeyChainStore.m: -------------------------------------------------------------------------------- 1 | // 2 | // UICKeyChainStore.m 3 | // UICKeyChainStore 4 | // 5 | // Created by Kishikawa Katsumi on 11/11/20. 6 | // Copyright (c) 2011 Kishikawa Katsumi. All rights reserved. 7 | // 8 | 9 | #import "UICKeyChainStore.h" 10 | 11 | static NSString *_defaultService; 12 | 13 | @interface UICKeyChainStore () { 14 | NSMutableDictionary *itemsToUpdate; 15 | } 16 | 17 | @end 18 | 19 | @implementation UICKeyChainStore 20 | 21 | + (NSString *)defaultService 22 | { 23 | if (!_defaultService) { 24 | _defaultService = [[NSBundle mainBundle] bundleIdentifier]; 25 | } 26 | 27 | return _defaultService; 28 | } 29 | 30 | + (void)setDefaultService:(NSString *)defaultService 31 | { 32 | _defaultService = defaultService; 33 | } 34 | 35 | #pragma mark - 36 | 37 | + (UICKeyChainStore *)keyChainStore 38 | { 39 | return [[self alloc] initWithService:[self defaultService]]; 40 | } 41 | 42 | + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service 43 | { 44 | return [[self alloc] initWithService:service]; 45 | } 46 | 47 | + (UICKeyChainStore *)keyChainStoreWithService:(NSString *)service accessGroup:(NSString *)accessGroup 48 | { 49 | return [[self alloc] initWithService:service accessGroup:accessGroup]; 50 | } 51 | 52 | - (instancetype)init 53 | { 54 | return [self initWithService:[self.class defaultService] accessGroup:nil]; 55 | } 56 | 57 | - (instancetype)initWithService:(NSString *)service 58 | { 59 | return [self initWithService:service accessGroup:nil]; 60 | } 61 | 62 | - (instancetype)initWithService:(NSString *)service accessGroup:(NSString *)accessGroup 63 | { 64 | self = [super init]; 65 | if (self) { 66 | if (!service) { 67 | service = [self.class defaultService]; 68 | } 69 | _service = [service copy]; 70 | _accessGroup = [accessGroup copy]; 71 | 72 | itemsToUpdate = [[NSMutableDictionary alloc] init]; 73 | } 74 | 75 | return self; 76 | } 77 | 78 | #pragma mark - 79 | 80 | + (NSString *)stringForKey:(NSString *)key 81 | { 82 | return [self stringForKey:key service:[self defaultService] accessGroup:nil]; 83 | } 84 | 85 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service 86 | { 87 | return [self stringForKey:key service:service accessGroup:nil]; 88 | } 89 | 90 | + (NSString *)stringForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 91 | { 92 | NSData *data = [self dataForKey:key service:service accessGroup:accessGroup]; 93 | if (data) { 94 | return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 95 | } 96 | 97 | return nil; 98 | } 99 | 100 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key 101 | { 102 | return [self setString:value forKey:key service:[self defaultService] accessGroup:nil]; 103 | } 104 | 105 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service 106 | { 107 | return [self setString:value forKey:key service:service accessGroup:nil]; 108 | } 109 | 110 | + (BOOL)setString:(NSString *)value forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 111 | { 112 | NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding]; 113 | return [self setData:data forKey:key service:service accessGroup:accessGroup]; 114 | } 115 | 116 | #pragma mark - 117 | 118 | + (NSData *)dataForKey:(NSString *)key 119 | { 120 | return [self dataForKey:key service:[self defaultService] accessGroup:nil]; 121 | } 122 | 123 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service 124 | { 125 | return [self dataForKey:key service:service accessGroup:nil]; 126 | } 127 | 128 | + (NSData *)dataForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 129 | { 130 | if (!key) { 131 | return nil; 132 | } 133 | if (!service) { 134 | service = [self defaultService]; 135 | } 136 | 137 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 138 | [query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 139 | [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 140 | [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 141 | [query setObject:service forKey:(__bridge id)kSecAttrService]; 142 | [query setObject:key forKey:(__bridge id)kSecAttrGeneric]; 143 | [query setObject:key forKey:(__bridge id)kSecAttrAccount]; 144 | #if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 145 | if (accessGroup) { 146 | [query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup]; 147 | } 148 | #endif 149 | 150 | CFTypeRef data = nil; 151 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &data); 152 | if (status != errSecSuccess) { 153 | return nil; 154 | } 155 | 156 | NSData *ret = [NSData dataWithData:(__bridge NSData *)data]; 157 | if (data) { 158 | CFRelease(data); 159 | } 160 | 161 | return ret; 162 | } 163 | 164 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key 165 | { 166 | return [self setData:data forKey:key service:[self defaultService] accessGroup:nil]; 167 | } 168 | 169 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service 170 | { 171 | return [self setData:data forKey:key service:service accessGroup:nil]; 172 | } 173 | 174 | + (BOOL)setData:(NSData *)data forKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 175 | { 176 | if (!key) { 177 | return NO; 178 | } 179 | if (!service) { 180 | service = [self defaultService]; 181 | } 182 | 183 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 184 | [query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 185 | [query setObject:service forKey:(__bridge id)kSecAttrService]; 186 | [query setObject:key forKey:(__bridge id)kSecAttrGeneric]; 187 | [query setObject:key forKey:(__bridge id)kSecAttrAccount]; 188 | #if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 189 | if (accessGroup) { 190 | [query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup]; 191 | } 192 | #endif 193 | 194 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL); 195 | if (status == errSecSuccess) { 196 | if (data) { 197 | NSMutableDictionary *attributesToUpdate = [[NSMutableDictionary alloc] init]; 198 | [attributesToUpdate setObject:data forKey:(__bridge id)kSecValueData]; 199 | 200 | status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributesToUpdate); 201 | if (status != errSecSuccess) { 202 | return NO; 203 | } 204 | } else { 205 | [self removeItemForKey:key service:service accessGroup:accessGroup]; 206 | } 207 | } else if (status == errSecItemNotFound) { 208 | NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init]; 209 | [attributes setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 210 | [attributes setObject:service forKey:(__bridge id)kSecAttrService]; 211 | [attributes setObject:key forKey:(__bridge id)kSecAttrGeneric]; 212 | [attributes setObject:key forKey:(__bridge id)kSecAttrAccount]; 213 | #if TARGET_OS_IPHONE || (defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9) 214 | [attributes setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible]; 215 | #endif 216 | [attributes setObject:data forKey:(__bridge id)kSecValueData]; 217 | #if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 218 | if (accessGroup) { 219 | [attributes setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup]; 220 | } 221 | #endif 222 | 223 | status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL); 224 | if (status != errSecSuccess) { 225 | return NO; 226 | } 227 | } else { 228 | return NO; 229 | } 230 | 231 | return YES; 232 | } 233 | 234 | #pragma mark - 235 | 236 | - (void)setString:(NSString *)string forKey:(NSString *)key 237 | { 238 | [self setData:[string dataUsingEncoding:NSUTF8StringEncoding] forKey:key]; 239 | } 240 | 241 | - (NSString *)stringForKey:(id)key 242 | { 243 | NSData *data = [self dataForKey:key]; 244 | if (data) { 245 | return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 246 | } 247 | 248 | return nil; 249 | } 250 | 251 | #pragma mark - 252 | 253 | - (void)setData:(NSData *)data forKey:(NSString *)key 254 | { 255 | if (!key) { 256 | return; 257 | } 258 | if (!data) { 259 | [self removeItemForKey:key]; 260 | } else { 261 | [itemsToUpdate setObject:data forKey:key]; 262 | } 263 | } 264 | 265 | - (NSData *)dataForKey:(NSString *)key 266 | { 267 | NSData *data = [itemsToUpdate objectForKey:key]; 268 | if (!data) { 269 | data = [[self class] dataForKey:key service:self.service accessGroup:self.accessGroup]; 270 | } 271 | 272 | return data; 273 | } 274 | 275 | #pragma mark - 276 | 277 | + (BOOL)removeItemForKey:(NSString *)key 278 | { 279 | return [UICKeyChainStore removeItemForKey:key service:[self defaultService] accessGroup:nil]; 280 | } 281 | 282 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service 283 | { 284 | return [UICKeyChainStore removeItemForKey:key service:service accessGroup:nil]; 285 | } 286 | 287 | + (BOOL)removeItemForKey:(NSString *)key service:(NSString *)service accessGroup:(NSString *)accessGroup 288 | { 289 | if (!key) { 290 | return NO; 291 | } 292 | if (!service) { 293 | service = [self defaultService]; 294 | } 295 | 296 | NSMutableDictionary *itemToDelete = [[NSMutableDictionary alloc] init]; 297 | [itemToDelete setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 298 | [itemToDelete setObject:service forKey:(__bridge id)kSecAttrService]; 299 | [itemToDelete setObject:key forKey:(__bridge id)kSecAttrGeneric]; 300 | [itemToDelete setObject:key forKey:(__bridge id)kSecAttrAccount]; 301 | #if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 302 | if (accessGroup) { 303 | [itemToDelete setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup]; 304 | } 305 | #endif 306 | 307 | OSStatus status = SecItemDelete((__bridge CFDictionaryRef)itemToDelete); 308 | if (status != errSecSuccess && status != errSecItemNotFound) { 309 | return NO; 310 | } 311 | 312 | return YES; 313 | } 314 | 315 | + (NSArray *)itemsForService:(NSString *)service accessGroup:(NSString *)accessGroup 316 | { 317 | if (!service) { 318 | service = [self defaultService]; 319 | } 320 | 321 | NSMutableDictionary *query = [[NSMutableDictionary alloc] init]; 322 | [query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 323 | [query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes]; 324 | [query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 325 | [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; 326 | [query setObject:service forKey:(__bridge id)kSecAttrService]; 327 | #if !TARGET_IPHONE_SIMULATOR && defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 328 | if (accessGroup) { 329 | [query setObject:accessGroup forKey:(__bridge id)kSecAttrAccessGroup]; 330 | } 331 | #endif 332 | 333 | CFArrayRef result = nil; 334 | OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); 335 | if (status == errSecSuccess || status == errSecItemNotFound) { 336 | return CFBridgingRelease(result); 337 | } else { 338 | return nil; 339 | } 340 | } 341 | 342 | + (BOOL)removeAllItems 343 | { 344 | return [self removeAllItemsForService:[self defaultService] accessGroup:nil]; 345 | } 346 | 347 | + (BOOL)removeAllItemsForService:(NSString *)service 348 | { 349 | return [self removeAllItemsForService:service accessGroup:nil]; 350 | } 351 | 352 | + (BOOL)removeAllItemsForService:(NSString *)service accessGroup:(NSString *)accessGroup 353 | { 354 | NSArray *items = [UICKeyChainStore itemsForService:service accessGroup:accessGroup]; 355 | for (NSDictionary *item in items) { 356 | NSMutableDictionary *itemToDelete = [[NSMutableDictionary alloc] initWithDictionary:item]; 357 | [itemToDelete setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 358 | 359 | OSStatus status = SecItemDelete((__bridge CFDictionaryRef)itemToDelete); 360 | if (status != errSecSuccess) { 361 | return NO; 362 | } 363 | } 364 | 365 | return YES; 366 | } 367 | 368 | #pragma mark - 369 | 370 | - (void)removeItemForKey:(NSString *)key 371 | { 372 | if ([itemsToUpdate objectForKey:key]) { 373 | [itemsToUpdate removeObjectForKey:key]; 374 | } else { 375 | [[self class] removeItemForKey:key service:self.service accessGroup:self.accessGroup]; 376 | } 377 | } 378 | 379 | - (void)removeAllItems 380 | { 381 | [itemsToUpdate removeAllObjects]; 382 | [[self class] removeAllItemsForService:self.service accessGroup:self.accessGroup]; 383 | } 384 | 385 | #pragma mark - 386 | 387 | - (void)synchronize 388 | { 389 | for (NSString *key in itemsToUpdate) { 390 | [[self class] setData:[itemsToUpdate objectForKey:key] forKey:key service:self.service accessGroup:self.accessGroup]; 391 | } 392 | 393 | [itemsToUpdate removeAllObjects]; 394 | } 395 | 396 | #pragma mark - 397 | 398 | - (NSString *)description 399 | { 400 | NSArray *items = [UICKeyChainStore itemsForService:self.service accessGroup:self.accessGroup]; 401 | NSMutableArray *list = [[NSMutableArray alloc] initWithCapacity:items.count]; 402 | for (NSDictionary *attributes in items) { 403 | NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; 404 | [attrs setObject:[attributes objectForKey:(__bridge id)kSecAttrService] forKey:@"Service"]; 405 | [attrs setObject:[attributes objectForKey:(__bridge id)kSecAttrAccount] forKey:@"Account"]; 406 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 407 | [attrs setObject:[attributes objectForKey:(__bridge id)kSecAttrAccessGroup] forKey:@"AccessGroup"]; 408 | #endif 409 | NSData *data = [attributes objectForKey:(__bridge id)kSecValueData]; 410 | NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 411 | if (string) { 412 | [attrs setObject:string forKey:@"Value"]; 413 | } else { 414 | [attrs setObject:data forKey:@"Value"]; 415 | } 416 | [list addObject:attrs]; 417 | } 418 | 419 | return [list description]; 420 | } 421 | 422 | #pragma mark - Object Subscripting 423 | 424 | - (NSString *)objectForKeyedSubscript:(NSString *)key 425 | { 426 | return [self stringForKey:key]; 427 | } 428 | 429 | - (void)setObject:(NSString *)obj forKeyedSubscript:(NSString *)key 430 | { 431 | [self setString:obj forKey:key]; 432 | } 433 | 434 | @end 435 | -------------------------------------------------------------------------------- /src/wp/UniqueDeviceID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Windows.Phone.System.Analytics; 3 | using WPCordovaClassLib.Cordova; 4 | using WPCordovaClassLib.Cordova.Commands; 5 | using WPCordovaClassLib.Cordova.JSON; 6 | 7 | namespace WPCordovaClassLib.Cordova.Commands 8 | { 9 | public class UniqueDeviceID : BaseCommand 10 | { 11 | public void get(string options) 12 | { 13 | 14 | string uuid = Windows.Phone.System.Analytics.HostInformation.PublisherHostId; 15 | uuid = uuid.Substring(0, 32); 16 | 17 | DispatchCommandResult(new PluginResult(PluginResult.Status.OK, uuid)); 18 | 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /www/uniqueid.js: -------------------------------------------------------------------------------- 1 | var exec = require('cordova/exec'); 2 | 3 | module.exports = { 4 | 5 | get: function(success, fail) { 6 | cordova.exec(success, fail, 'UniqueDeviceID', 'get', []); 7 | } 8 | 9 | }; --------------------------------------------------------------------------------