1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.lowpan; 18 19 import android.annotation.NonNull; 20 import android.icu.text.StringPrep; 21 import android.icu.text.StringPrepParseException; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.util.Log; 25 import com.android.internal.util.HexDump; 26 import java.nio.charset.StandardCharsets; 27 import java.util.Arrays; 28 import java.util.Objects; 29 30 /** 31 * Describes an instance of a LoWPAN network. 32 * 33 * @hide 34 */ 35 // @SystemApi 36 public class LowpanIdentity implements Parcelable { 37 private static final String TAG = LowpanIdentity.class.getSimpleName(); 38 39 // Constants 40 public static final int UNSPECIFIED_CHANNEL = -1; 41 public static final int UNSPECIFIED_PANID = 0xFFFFFFFF; 42 // Builder 43 44 /** @hide */ 45 // @SystemApi 46 public static class Builder { 47 private static final StringPrep stringPrep = 48 StringPrep.getInstance(StringPrep.RFC3920_RESOURCEPREP); 49 50 final LowpanIdentity mIdentity = new LowpanIdentity(); 51 52 private static String escape(@NonNull byte[] bytes) { 53 StringBuffer sb = new StringBuffer(); 54 for (byte b : bytes) { 55 if (b >= 32 && b <= 126) { 56 sb.append((char) b); 57 } else { 58 sb.append(String.format("\\0x%02x", b & 0xFF)); 59 } 60 } 61 return sb.toString(); 62 } 63 64 public Builder setLowpanIdentity(@NonNull LowpanIdentity x) { 65 Objects.requireNonNull(x); 66 setRawName(x.getRawName()); 67 setXpanid(x.getXpanid()); 68 setPanid(x.getPanid()); 69 setChannel(x.getChannel()); 70 setType(x.getType()); 71 return this; 72 } 73 74 public Builder setName(@NonNull String name) { 75 Objects.requireNonNull(name); 76 try { 77 mIdentity.mName = stringPrep.prepare(name, StringPrep.DEFAULT); 78 mIdentity.mRawName = mIdentity.mName.getBytes(StandardCharsets.UTF_8); 79 mIdentity.mIsNameValid = true; 80 } catch (StringPrepParseException x) { 81 Log.w(TAG, x.toString()); 82 setRawName(name.getBytes(StandardCharsets.UTF_8)); 83 } 84 return this; 85 } 86 87 public Builder setRawName(@NonNull byte[] name) { 88 Objects.requireNonNull(name); 89 mIdentity.mRawName = name.clone(); 90 mIdentity.mName = new String(name, StandardCharsets.UTF_8); 91 try { 92 String nameCheck = stringPrep.prepare(mIdentity.mName, StringPrep.DEFAULT); 93 mIdentity.mIsNameValid = 94 Arrays.equals(nameCheck.getBytes(StandardCharsets.UTF_8), name); 95 } catch (StringPrepParseException x) { 96 Log.w(TAG, x.toString()); 97 mIdentity.mIsNameValid = false; 98 } 99 100 // Non-normal names must be rendered differently to avoid confusion. 101 if (!mIdentity.mIsNameValid) { 102 mIdentity.mName = "" + escape(name) + ""; 103 } 104 105 return this; 106 } 107 108 public Builder setXpanid(byte x[]) { 109 mIdentity.mXpanid = (x != null ? x.clone() : null); 110 return this; 111 } 112 113 public Builder setPanid(int x) { 114 mIdentity.mPanid = x; 115 return this; 116 } 117 118 public Builder setType(@NonNull String x) { 119 mIdentity.mType = x; 120 return this; 121 } 122 123 public Builder setChannel(int x) { 124 mIdentity.mChannel = x; 125 return this; 126 } 127 128 public LowpanIdentity build() { 129 return mIdentity; 130 } 131 } 132 133 LowpanIdentity() {} 134 135 // Instance Variables 136 137 private String mName = ""; 138 private boolean mIsNameValid = true; 139 private byte[] mRawName = new byte[0]; 140 private String mType = ""; 141 private byte[] mXpanid = new byte[0]; 142 private int mPanid = UNSPECIFIED_PANID; 143 private int mChannel = UNSPECIFIED_CHANNEL; 144 145 // Public Getters 146 147 public String getName() { 148 return mName; 149 } 150 151 public boolean isNameValid() { 152 return mIsNameValid; 153 } 154 155 public byte[] getRawName() { 156 return mRawName.clone(); 157 } 158 159 public byte[] getXpanid() { 160 return mXpanid.clone(); 161 } 162 163 public int getPanid() { 164 return mPanid; 165 } 166 167 public String getType() { 168 return mType; 169 } 170 171 public int getChannel() { 172 return mChannel; 173 } 174 175 @Override 176 public String toString() { 177 StringBuffer sb = new StringBuffer(); 178 179 sb.append("Name:").append(getName()); 180 181 if (mType.length() > 0) { 182 sb.append(", Type:").append(mType); 183 } 184 185 if (mXpanid.length > 0) { 186 sb.append(", XPANID:").append(HexDump.toHexString(mXpanid)); 187 } 188 189 if (mPanid != UNSPECIFIED_PANID) { 190 sb.append(", PANID:").append(String.format("0x%04X", mPanid)); 191 } 192 193 if (mChannel != UNSPECIFIED_CHANNEL) { 194 sb.append(", Channel:").append(mChannel); 195 } 196 197 return sb.toString(); 198 } 199 200 @Override 201 public boolean equals(Object obj) { 202 if (!(obj instanceof LowpanIdentity)) { 203 return false; 204 } 205 LowpanIdentity rhs = (LowpanIdentity) obj; 206 return Arrays.equals(mRawName, rhs.mRawName) 207 && Arrays.equals(mXpanid, rhs.mXpanid) 208 && mType.equals(rhs.mType) 209 && mPanid == rhs.mPanid 210 && mChannel == rhs.mChannel; 211 } 212 213 @Override 214 public int hashCode() { 215 return Objects.hash( 216 Arrays.hashCode(mRawName), mType, Arrays.hashCode(mXpanid), mPanid, mChannel); 217 } 218 219 /** Implement the Parcelable interface. */ 220 @Override 221 public int describeContents() { 222 return 0; 223 } 224 225 /** Implement the Parcelable interface. */ 226 @Override 227 public void writeToParcel(Parcel dest, int flags) { 228 dest.writeByteArray(mRawName); 229 dest.writeString(mType); 230 dest.writeByteArray(mXpanid); 231 dest.writeInt(mPanid); 232 dest.writeInt(mChannel); 233 } 234 235 /** Implement the Parcelable interface. */ 236 public static final Creator<LowpanIdentity> CREATOR = 237 new Creator<LowpanIdentity>() { 238 239 public LowpanIdentity createFromParcel(Parcel in) { 240 Builder builder = new Builder(); 241 242 builder.setRawName(in.createByteArray()); 243 builder.setType(in.readString()); 244 builder.setXpanid(in.createByteArray()); 245 builder.setPanid(in.readInt()); 246 builder.setChannel(in.readInt()); 247 248 return builder.build(); 249 } 250 251 public LowpanIdentity[] newArray(int size) { 252 return new LowpanIdentity[size]; 253 } 254 }; 255 } 256