155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync/* 255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * Copyright (C) 2011 The Android Open Source Project 355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * 455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * Licensed under the Apache License, Version 2.0 (the "License"); 555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * you may not use this file except in compliance with the License. 655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * You may obtain a copy of the License at 755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * 855bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * http://www.apache.org/licenses/LICENSE-2.0 955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * 1055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * Unless required by applicable law or agreed to in writing, software 1155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * distributed under the License is distributed on an "AS IS" BASIS, 1255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * See the License for the specific language governing permissions and 1455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync * limitations under the License. 1555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync */ 1655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 1755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncpackage android.net.wifi.p2p; 1855bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 1955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport android.os.Parcelable; 2055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport android.os.Parcel; 2155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport android.net.wifi.p2p.WifiP2pDevice; 2257e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriffimport android.text.TextUtils; 2355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport android.util.Log; 2455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 2555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport java.util.ArrayList; 2655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport java.util.Collection; 2755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncimport java.util.Collections; 2821ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriffimport java.util.HashMap; 2955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 3055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync/** 31bfb27bbefb013220af699881d486cc04be5ec1f5Yoshihiko Ikenaga * A class representing a Wi-Fi P2p device list. 326f7d385d964949e507dcc9c88012372f48d0bce7Irfan Sheriff * 33bfb27bbefb013220af699881d486cc04be5ec1f5Yoshihiko Ikenaga * Note that the operations are not thread safe. 346f7d385d964949e507dcc9c88012372f48d0bce7Irfan Sheriff * {@see WifiP2pManager} 3555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync */ 3655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo syncpublic class WifiP2pDeviceList implements Parcelable { 3755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 38d8544a51482c86b12da3ac82ea77b83045f689b7Jeff Brown private final HashMap<String, WifiP2pDevice> mDevices = new HashMap<String, WifiP2pDevice>(); 3955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 40651cdfcbac6245f570475991588ddc2d30265e8dIrfan Sheriff public WifiP2pDeviceList() { 4155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 4255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 43651cdfcbac6245f570475991588ddc2d30265e8dIrfan Sheriff /** copy constructor */ 4455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public WifiP2pDeviceList(WifiP2pDeviceList source) { 4555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync if (source != null) { 4621ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff for (WifiP2pDevice d : source.getDeviceList()) { 47ab3b9fbfa0523e036ec71888d0da5dc55cd3301bIrfan Sheriff mDevices.put(d.deviceAddress, new WifiP2pDevice(d)); 4821ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff } 4955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 5055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 5155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 526f7d385d964949e507dcc9c88012372f48d0bce7Irfan Sheriff /** @hide */ 5355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public WifiP2pDeviceList(ArrayList<WifiP2pDevice> devices) { 5455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync for (WifiP2pDevice device : devices) { 5521ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff if (device.deviceAddress != null) { 56ab3b9fbfa0523e036ec71888d0da5dc55cd3301bIrfan Sheriff mDevices.put(device.deviceAddress, new WifiP2pDevice(device)); 5721ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff } 5855bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 5955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 6055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 61f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff private void validateDevice(WifiP2pDevice device) { 62f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff if (device == null) throw new IllegalArgumentException("Null device"); 63f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff if (TextUtils.isEmpty(device.deviceAddress)) { 64f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff throw new IllegalArgumentException("Empty deviceAddress"); 65f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff } 66f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff } 67f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff 68f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff private void validateDeviceAddress(String deviceAddress) { 69f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff if (TextUtils.isEmpty(deviceAddress)) { 70f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff throw new IllegalArgumentException("Empty deviceAddress"); 71f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff } 72f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff } 73f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff 743a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff /** Clear the list @hide */ 75ea5b16ac5751022de73e8f1225407eb01e7f1824Irfan Sheriff public boolean clear() { 76ea5b16ac5751022de73e8f1225407eb01e7f1824Irfan Sheriff if (mDevices.isEmpty()) return false; 7755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync mDevices.clear(); 78ea5b16ac5751022de73e8f1225407eb01e7f1824Irfan Sheriff return true; 7955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 8055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 813a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff /** 823a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * Add/update a device to the list. If the device is not found, a new device entry 833a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * is created. If the device is already found, the device details are updated 843a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @param device to be updated 853a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @hide 863a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff */ 874be4d31f34a0fd0e23de1cbda311c07412f8d0b8Irfan Sheriff public void update(WifiP2pDevice device) { 883a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff updateSupplicantDetails(device); 893a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff mDevices.get(device.deviceAddress).status = device.status; 903a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff } 913a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff 923a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff /** Only updates details fetched from the supplicant @hide */ 933a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff void updateSupplicantDetails(WifiP2pDevice device) { 94f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDevice(device); 9521ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff WifiP2pDevice d = mDevices.get(device.deviceAddress); 9621ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff if (d != null) { 9721ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff d.deviceName = device.deviceName; 9821ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff d.primaryDeviceType = device.primaryDeviceType; 9921ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff d.secondaryDeviceType = device.secondaryDeviceType; 10021ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff d.wpsConfigMethodsSupported = device.wpsConfigMethodsSupported; 10121ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff d.deviceCapability = device.deviceCapability; 10221ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff d.groupCapability = device.groupCapability; 103e298d884580006cbcd4aec8fd7877dae3f081eecIrfan Sheriff d.wfdInfo = device.wfdInfo; 10421ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff return; 1054be4d31f34a0fd0e23de1cbda311c07412f8d0b8Irfan Sheriff } 1064be4d31f34a0fd0e23de1cbda311c07412f8d0b8Irfan Sheriff //Not found, add a new one 10721ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff mDevices.put(device.deviceAddress, device); 10821ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff } 10921ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff 11021ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff /** @hide */ 1113a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff void updateGroupCapability(String deviceAddress, int groupCapab) { 112f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDeviceAddress(deviceAddress); 11357e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff WifiP2pDevice d = mDevices.get(deviceAddress); 11457e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff if (d != null) { 11557e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff d.groupCapability = groupCapab; 11657e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff } 11757e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff } 11857e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff 11957e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff /** @hide */ 1203a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff void updateStatus(String deviceAddress, int status) { 121f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDeviceAddress(deviceAddress); 122bfb27bbefb013220af699881d486cc04be5ec1f5Yoshihiko Ikenaga WifiP2pDevice d = mDevices.get(deviceAddress); 12357e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff if (d != null) { 12457e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff d.status = status; 12557e42f4117e92c03d39f1e1e572f53ef5bb821b8Irfan Sheriff } 126bfb27bbefb013220af699881d486cc04be5ec1f5Yoshihiko Ikenaga } 127bfb27bbefb013220af699881d486cc04be5ec1f5Yoshihiko Ikenaga 1283a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff /** 1293a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * Fetch a device from the list 1303a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @param deviceAddress is the address of the device 1313a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @return WifiP2pDevice device found, or null if none found 1323a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff */ 13321ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff public WifiP2pDevice get(String deviceAddress) { 134f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDeviceAddress(deviceAddress); 13521ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff return mDevices.get(deviceAddress); 13655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 13755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 1386f7d385d964949e507dcc9c88012372f48d0bce7Irfan Sheriff /** @hide */ 13955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public boolean remove(WifiP2pDevice device) { 140f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDevice(device); 14121ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff return mDevices.remove(device.deviceAddress) != null; 14255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 14355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 1443a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff /** 1453a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * Remove a device from the list 1463a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @param deviceAddress is the address of the device 1473a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @return WifiP2pDevice device removed, or null if none removed 1483a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff * @hide 1493a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff */ 1503a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff public WifiP2pDevice remove(String deviceAddress) { 151f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDeviceAddress(deviceAddress); 1523a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff return mDevices.remove(deviceAddress); 1533a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff } 1543a67e2515bff73fab57621b1f9966662e83b7881Irfan Sheriff 15541de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff /** Returns true if any device the list was removed @hide */ 15641de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff public boolean remove(WifiP2pDeviceList list) { 15741de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff boolean ret = false; 15841de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff for (WifiP2pDevice d : list.mDevices.values()) { 15941de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff if (remove(d)) ret = true; 16041de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff } 16141de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff return ret; 16241de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff } 16341de2404658c7c6faf6c78e777ba50af11784f5cIrfan Sheriff 1646f7d385d964949e507dcc9c88012372f48d0bce7Irfan Sheriff /** Get the list of devices */ 16555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public Collection<WifiP2pDevice> getDeviceList() { 16621ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff return Collections.unmodifiableCollection(mDevices.values()); 16755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 16855bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 16910ca870d3b58ec6fd62b85466ec1211fca77d33eIrfan Sheriff /** @hide */ 17010ca870d3b58ec6fd62b85466ec1211fca77d33eIrfan Sheriff public boolean isGroupOwner(String deviceAddress) { 171f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff validateDeviceAddress(deviceAddress); 172f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff WifiP2pDevice device = mDevices.get(deviceAddress); 173f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff if (device == null) { 174f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff throw new IllegalArgumentException("Device not found " + deviceAddress); 17510ca870d3b58ec6fd62b85466ec1211fca77d33eIrfan Sheriff } 176f118043ca726c65f04cddc6321c9e820b577fc6fIrfan Sheriff return device.isGroupOwner(); 17710ca870d3b58ec6fd62b85466ec1211fca77d33eIrfan Sheriff } 17810ca870d3b58ec6fd62b85466ec1211fca77d33eIrfan Sheriff 17955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public String toString() { 18055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync StringBuffer sbuf = new StringBuffer(); 18121ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff for (WifiP2pDevice device : mDevices.values()) { 18255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync sbuf.append("\n").append(device); 18355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 18455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync return sbuf.toString(); 18555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 18655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 187651cdfcbac6245f570475991588ddc2d30265e8dIrfan Sheriff /** Implement the Parcelable interface */ 18855bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public int describeContents() { 18955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync return 0; 19055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 19155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 192651cdfcbac6245f570475991588ddc2d30265e8dIrfan Sheriff /** Implement the Parcelable interface */ 19355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public void writeToParcel(Parcel dest, int flags) { 19455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync dest.writeInt(mDevices.size()); 19521ba8153325e010224c6bc75a0acdc98b6ca82e8Irfan Sheriff for(WifiP2pDevice device : mDevices.values()) { 19655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync dest.writeParcelable(device, flags); 19755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 19855bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 19955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 200651cdfcbac6245f570475991588ddc2d30265e8dIrfan Sheriff /** Implement the Parcelable interface */ 20155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public static final Creator<WifiP2pDeviceList> CREATOR = 20255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync new Creator<WifiP2pDeviceList>() { 20355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public WifiP2pDeviceList createFromParcel(Parcel in) { 20455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync WifiP2pDeviceList deviceList = new WifiP2pDeviceList(); 20555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 20655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync int deviceCount = in.readInt(); 20755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync for (int i = 0; i < deviceCount; i++) { 2084be4d31f34a0fd0e23de1cbda311c07412f8d0b8Irfan Sheriff deviceList.update((WifiP2pDevice)in.readParcelable(null)); 20955bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 21055bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync return deviceList; 21155bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 21255bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync 21355bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync public WifiP2pDeviceList[] newArray(int size) { 21455bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync return new WifiP2pDeviceList[size]; 21555bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync } 21655bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync }; 21755bc5f3e0408bcb5a39a6732de0b2d1aa99a55berepo sync} 218